#include "head.h"
#include "Scythe.h"
#include "utilityfunctions.h"
#include "ogreSkeletonSetup.h"

//this is the setup function where we store all the bones in a list, in such a way that
//we can iterate through them in hierarchal order
bool ScytheSkeleton::setup(Ogre::Entity* ent, Scythe::SkeletonData* skelData)
{
	if(!ent->hasSkeleton())
		return false;
	skel = ent->getSkeleton();
	if(!skel)
		return false;

	entity = ent;
	scytheData = skelData;

	//fill the bone list with every bone we have
	Ogre::SkeletonInstance::BoneIterator itr = skel->getBoneIterator();
	while( itr.hasMoreElements())
	{
		Ogre::Bone* b = itr.getNext();
		ScytheBone* sb = new ScytheBone;
		sb->bone		= b;
		sb->name		= b->getName();
		bonelist.push_back( sb);

		b->setManuallyControlled(true);
		sb->isActor		= false;
		sb->skeleton		= this;
	}


	//all bones now reference itself to parent
	for (int i = 0; i < bonelist.size(); i++)
	{
		ScytheBone* sb = bonelist[i];
		if(sb->bone->getParent() == NULL) 
		{
			sb->isRoot	= true;
			rootBone	= sb;
		} else {

			sb->isRoot	 = false;
			ScytheBone* parent = getBone(sb->bone->getParent()->getName());
			if (parent)
				parent->childBones.push_back(sb);
		}
	}

	return true;
}

//a handy utility function
ScytheBone*	ScytheSkeleton::getBone(string name)
{
	for (unsigned int i = 0; i < bonelist.size(); i++)
	{
		if(bonelist[i]->name == name)
			return bonelist[i];
	}
	return 0;
}

//this is technically inverse ragdoll.  It matches the position of the ragdoll primitives to the 
//position of the character bones.  Can be used at ragdoll creation, to make the ragdoll
//match the character before collapsing.
void ScytheSkeleton::setRagdollToSkeleton()
{
	Ogre::Matrix3 rot, m;
	Ogre::Vector3 pos;
	for (unsigned int i = 0; i < bonelist.size(); i++)
	{
		bonelist[i]->bone->getWorldOrientation().ToRotationMatrix( m );
		entity->getParentSceneNode()->getWorldOrientation().ToRotationMatrix( rot );
		rot = rot * m;
		pos = bonelist[i]->bone->getWorldPosition() + entity->getParentSceneNode()->getWorldPosition();

		scytheData->setBonePosition(bonelist[i]->name, toScy( pos ), toScy( rot ) );
	}
}


void ScytheSkeleton::updateRagdoll()
{
	//do the bones transformation
	rootBone->render(scytheData, 
		entity->getParentSceneNode()->getWorldPosition(), 
		entity->getParentSceneNode()->getWorldOrientation());

	//Ogre's command to forcibly "render" the skeleton prematurely
	//this allow us to attach objects to the bones without a frame-lag
	entity->_updateAnimation();

	//render things dependent on bone positions
	rootBone->renderDependants();
}
/*
This is where the ragdoll calculations go down
*/
void ScytheBone::render(Scythe::SkeletonData* skeletalID, Ogre::Vector3 modelsPos, Ogre::Quaternion modelsRot, const Ogre::Quaternion parentsInitialWorldOrientation)
{
	char* name2 = new char[500];
	strcpy(name2, name.c_str());

	if(isActor)
	{
		//set the orientation of the bone
		Ogre::Matrix3 m33 = toOgre(skeletalID->getBoneOrientation( name2 ));
		Ogre::Quaternion targetRot(m33);

		targetRot  = targetRot * modelsRot;

		bone->setOrientation(bone->getInitialOrientation());

		if (!isRoot)
		{
			Ogre::Quaternion parentalOffset = 
				bone->getParent()->getWorldOrientation().Inverse() * parentsInitialWorldOrientation;	
			bone->rotate(parentalOffset, Ogre::Node::TS_PARENT);
		}

		bone->rotate(targetRot, Ogre::Node::TS_WORLD);
		bone->rotate(modelsRot.Inverse(), Ogre::Node::TS_WORLD);
	}
	else {
		bone->setOrientation(Ogre::Quaternion::IDENTITY);
	}

	//only the root bone has its position affected
	if(isRoot) 
	{
		if (isActor)
		{
			Ogre::Vector3 startPos = bone->getWorldPosition() + modelsPos;
			
			Ogre::Vector3 rigidPos = toOgre(skeletalID->getBonePosition(name2));	//physics entity position
			//instead of moving the bone here, we will move the model

			skeleton->entity->getParentSceneNode()->setPosition(
				modelsRot.Inverse() * (rigidPos) );
		}
	}

	delete [] name2;

	//recurse through all the children
	for (int i = 0; i < childBones.size(); i++)
	{
		childBones[i]->render(skeletalID, modelsPos, modelsRot, parentsInitialWorldOrientation * bone->getInitialOrientation());
	}
}


//positioning of objects that are dependant on the bone's position
void ScytheBone::renderDependants()
{/*
	Ogre::Matrix3 boneRot;
	Ogre::Quaternion boneRotQ;
	Ogre::Vector3 bonePos;

	//here we get the global transformation of the bone
	if(dependantActor.size() > 0)
	{
		//get the bones rotation and rotate by the models rotation to convert to world space
		boneRotQ = bone->getWorldOrientation() * skeleton->entity->getParentSceneNode()->getWorldOrientation();
		//convert to matrix
		boneRotQ.ToRotationMatrix(boneRot);

		//get bone position
		bonePos = bone->getWorldPosition();
		//add models position
		bonePos += skeleton->entity->getParentSceneNode()->getWorldPosition();
	}

	//now we use it to calculate transformation of the models
	for(unsigned int i = 0; i < dependantActor.size(); i++)
	{
		//now we have the bones global position and rotation, use it to position the models
		Scythe::Vector3 pos = physics.getModelPos( models[dependantActor[i]]->scytheID, toScy(bonePos), toScy(boneRot) );
		//set position
		models[dependantActor[i]]->node->setPosition( pos[0], pos[1], pos[2] );
		//get orientation
		Scythe::Quat rot = physics.getModelRotQuat( models[dependantActor[i]]->scytheID, toScy(boneRot) );
		//set orientation
		models[dependantActor[i]]->node->setOrientation(rot.w, rot.x, rot.y, rot.z);
	}

	//recurse through all the children
	for (int i = 0; i < childBones.size(); i++)
	{
		childBones[i]->renderDependants();
	}
/**/}

//this function activates or deactivates each bone
void ScytheSkeleton::setBoneIsRag(bool rag, std::string boneName)
{
	getBone(boneName)->isActor = rag;
}

void	ScytheSkeleton::attachDependantModel(std::string boneName, int modelID)
{
	getBone(boneName)->dependantActor.push_back(modelID);
}

